/* Copyright (c) 2010, 2011 Damian Kmiecik
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
   * Neither the name of the copyright holders nor the names of
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. */

#ifndef TIMER_H_
#define TIMER_H_

#include <avr/io.h>
#include <inttypes.h>

#define TIMER_MIN			0.005F
#define TIMER_MIN_I			5UL
#define TIMER_NUMBER		2
#define TIMER_USE_INT32		0

#ifdef __AVR_ATmega128__
#define TIMER_TIMSK	TIMSK
#if TIMER_NUMBER == 0
#define TIMER_TCCR	TCCR0
#define TIMER_DIV	_BV(CS02)|_BV(CS01)|_BV(CS00)
#define TIMER_TCNT	TCNT0
#define TIMER_TOIE	_BV(TOIE0)
#define TIMER_ISR	TIMER0_OVF_vect
#define TIMER_CNT	256UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 1
#define TIMER_TCCR	TCCR1B
#define TIMER_DIV	_BV(CS12)|_BV(CS10)
#define TIMER_TCNT	TCNT1
#define TIMER_TOIE	_BV(TOIE1)
#define TIMER_ISR	TIMER1_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 2
#define TIMER_TCCR	TCCR2
#define TIMER_DIV	_BV(CS22)|_BV(CS20)
#define TIMER_TCNT	TCNT2
#define TIMER_TOIE	_BV(TOIE2)
#define TIMER_ISR	TIMER2_OVF_vect
#define TIMER_CNT	256UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 3
#define TIMER_TCCR	TCCR3B
#define TIMER_DIV	_BV(CS32)|_BV(CS30)
#define TIMER_TCNT	TCNT3
#define TIMER_TOIE	_BV(TOIE3)
#define TIMER_ISR	TIMER3_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#else
#error "Unknow timer number for ATmega128"
#endif
#endif

#ifdef __AVR_ATmega32__
#define TIMER_TIMSK	TIMSK
#if TIMER_NUMBER == 0
#define TIMER_TCCR	TCCR0
#define TIMER_DIV	_BV(CS02)|_BV(CS00)
#define TIMER_TCNT	TCNT0
#define TIMER_TOIE	_BV(TOIE0)
#define TIMER_ISR	TIMER0_OVF_vect
#define TIMER_CNT	256UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 1
#define TIMER_TCCR	TCCR1B
#define TIMER_DIV	_BV(CS12)|_BV(CS10)
#define TIMER_TCNT	TCNT1
#define TIMER_TOIE	_BV(TOIE1)
#define TIMER_ISR	TIMER1_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 2
#define TIMER_TCCR	TCCR2
#define TIMER_DIV	_BV(CS22)|_BV(CS21)|_BV(CS20)
#define TIMER_TCNT	TCNT2
#define TIMER_TOIE	_BV(TOIE2)
#define TIMER_ISR	TIMER2_OVF_vect
#define TIMER_CNT	256UL-(F_CPU*TIMER_MIN/1024UL)
#else
#error "Unknow timer number for ATmega32"
#endif
#endif

#ifdef __AVR_ATmega2560__
#if TIMER_NUMBER == 0
#define TIMER_TIMSK	TIMSK0
#define TIMER_TCCR	TCCR0B
#define TIMER_DIV	_BV(CS02)|_BV(CS00)
#define TIMER_TCNT	TCNT0
#define TIMER_TOIE	_BV(TOIE0)
#define TIMER_ISR	TIMER0_OVF_vect
#define TIMER_CNT	256UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 1
#define TIMER_TIMSK	TIMSK1
#define TIMER_TCCR	TCCR1B
#define TIMER_DIV	_BV(CS12)|_BV(CS10)
#define TIMER_TCNT	TCNT1
#define TIMER_TOIE	_BV(TOIE1)
#define TIMER_ISR	TIMER1_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 3
#define TIMER_TIMSK	TIMSK3
#define TIMER_TCCR	TCCR3B
#define TIMER_DIV	_BV(CS32)|_BV(CS30)
#define TIMER_TCNT	TCNT3
#define TIMER_TOIE	_BV(TOIE3)
#define TIMER_ISR	TIMER3_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 4
#define TIMER_TIMSK	TIMSK4
#define TIMER_TCCR	TCCR4B
#define TIMER_DIV	_BV(CS42)|_BV(CS40)
#define TIMER_TCNT	TCNT4
#define TIMER_TOIE	_BV(TOIE4)
#define TIMER_ISR	TIMER4_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 5
#define TIMER_TIMSK	TIMSK5
#define TIMER_TCCR	TCCR5B
#define TIMER_DIV	_BV(CS52)|_BV(CS50)
#define TIMER_TCNT	TCNT5
#define TIMER_TOIE	_BV(TOIE5)
#define TIMER_ISR	TIMER5_OVF_vect
#define TIMER_CNT	65536UL-(F_CPU*TIMER_MIN/1024UL)
#elif TIMER_NUMBER == 2
#define TIMER_TIMSK	TIMSK2
#define TIMER_TCCR	TCCR2B
#define TIMER_DIV	_BV(CS22)|_BV(CS21)|_BV(CS20)
#define TIMER_TCNT	TCNT2
#define TIMER_TOIE	_BV(TOIE2)
#define TIMER_ISR	TIMER2_OVF_vect
#define TIMER_CNT	256UL-(F_CPU*TIMER_MIN/1024UL)
#else
#error "Unknow timer number for ATmega32"
#endif
#endif

struct timer_item_s {
#if TIMER_USE_INT32
	uint32_t time;
	uint32_t cnt;
#else
	uint16_t time;
	uint16_t cnt;
#endif
};

#if TIMER_USE_INT32
extern volatile uint32_t timer_cnt;
#else
extern volatile uint16_t timer_cnt;
#endif

#define timerSet_ms(s,x)		timerSet_x(s,(x/TIMER_MIN_I))
#define timerSet_s(s,x)			timerSet_x(s,(x*1000UL)/TIMER_MIN_I)
#define timerSet_min(s,x)		timerSet_x(s,(x*1000UL*60UL)/TIMER_MIN_I)
#define timerSet_h(s,x)			timerSet_x(s,(x*1000UL*60UL*60UL)/TIMER_MIN_I)

#define timerExpired_within_ms(s,x)		timerExpired_within_x(s,(x/TIMER_MIN_I))
#define timerExpired_within_s(s,x)		timerExpired_within_x(s,(x*1000UL)/TIMER_MIN_I)
#define timerExpired_within_min(s,x)	timerExpired_within_x(s,(x*1000UL*60UL)/TIMER_MIN_I)
#define timerExpired_within_h(s,x)		timerExpired_within_x(s,(x*1000UL*60UL*60UL)/TIMER_MIN_I)

#define timerStart()	{TIMER_TCCR = TIMER_DIV; TIMER_TIMSK |= TIMER_TOIE; TIMER_TCNT = 0xFE;}
#define timerStop()		{TIMER_TCCR = 0;}

#define timerInit()	timerStart()
extern uint8_t timerExpired(const struct timer_item_s*);
extern uint8_t timerExpiredReset(struct timer_item_s *timer);
extern void timerReset(struct timer_item_s*);
#if TIMER_USE_INT32
extern void timerSet_x(struct timer_item_s*, uint32_t);
uint8_t timerExpired_within_x(const struct timer_item_s*, const uint32_t);
#else
extern void timerSet_x(struct timer_item_s*, uint16_t);
uint8_t timerExpired_within_x(const struct timer_item_s*, const uint16_t);
#endif

#endif /* TIMER_H_ */
